package com.putable.nmc.world;

/**
 * A 'Customer Information Token', describing a customer to the DMV.
 * <p>
 * CIT's are created by a {@link World} and passed to a DMV by the
 * {@link World#getNextCIT(double)}) method, and are returned to World by the
 * {@link World#exitInterview(CIT)} method.
 * <p>
 * Most of the state maintained in a CIT is read-only outside of CIT's own
 * package, specifically to indicate that DMV objects are not allowed to alter
 * that state.
 * <p>
 * There are two public mutator methods in class CIT. The first one is
 * {@link #setEntered(boolean)}, which should be called once on each CIT object,
 * with an argument of 'true' if the customer created from the given CIT object
 * actually entered the DMV, and false if that customer was immediately turned
 * away because the DMV was at capacity when the customer arrived.
 * <p>
 * The second public mutator method is {@link #setTaskCompleteAt(double)}, which
 * should be called at most once on again given CIT object, if and only if the
 * customer created from the given CIT object actually both entered the DMV and
 * then subsequently successfully completed the task they went to the DMV to do.
 * 
 * @author ackley
 * @version 1.0
 */

public class CIT {
    private CustomerType type;
    private double willTakeTime;

    private Task task;
    private boolean entered;
    private boolean inBuilding;
    private double arrivalTime;
    private double taskCompletedAt = -1;
    private double giveupTime;
    private boolean satisfied;
    private boolean complained;

    CIT(CustomerType type) {
        this.type = type;
    }

    /**
     * What kind of customer this CIT represented.
     * 
     * @return the customer type
     */
    public CustomerType getType() {
        return type;
    }

    void setType(CustomerType type) {
        this.type = type;
    }

    /**
     * How much time the task of the customer represented by this CIT will take,
     * once they begin to be served by a good teller.
     * 
     * @return the time it would take a good teller to perform the task
     */
    public double getWillTakeTime() {
        return willTakeTime;
    }

    void setWillTakeTime(double willTakeTime) {
        this.willTakeTime = willTakeTime;
    }

    boolean getComplained() {
        return complained;
    }

    void setComplained(boolean complained) {
        this.complained = complained;
    }

    boolean getInBuilding() {
        return inBuilding;
    }

    void setInBuilding(boolean inBuilding) {
        this.inBuilding = inBuilding;
    }

    boolean getSatisfied() {
        return satisfied;
    }

    void setSatisfied(boolean satisfied) {
        this.satisfied = satisfied;
    }

    /**
     * What kind of task the customer represented by this CIT wants to perform.
     * The task determines how much money the DMV will receive if and only if
     * the customer's task is completed successfully.
     * 
     * @return the task
     */
    public Task getTask() {
        return task;
    }

    void setTask(Task task) {
        this.task = task;
    }

    /**
     * When the customer represented by this CIT will actually enter the DMV.
     * That time will generally be in the future relative to the clock value
     * provided to {@link World#getNextCIT(double)}.
     * 
     * @return the arrival time
     */
    public double getArrivalTime() {
        return arrivalTime;
    }

    void setArrivalTime(double timeArrived) {
        this.arrivalTime = timeArrived;
    }

    /**
     * What time the customer represented by this CIT initially plans to give up
     * in disgust even if they have not completed their task. Note that there is
     * no public setter corresponding to this field. Even if a customer chooses
     * to stay longer (or shorter) in the DMV, for whatever reason, than this
     * value would indicate, this value does not get changed.
     * 
     * @return the give up time
     */
    public double getGiveupTime() {
        return giveupTime;
    }

    void setGiveupTime(double timeLeft) {
        this.giveupTime = timeLeft;
    }

    double getTaskCompletedAt() {
        return taskCompletedAt;
    }

    /**
     * Specify when the customer represented by this CIT successfully finished
     * their task. This method should be called no more than once on any given
     * CIT object, and should be called only when the customer finishes their
     * task successfully. If the customer fails to complete their task before
     * giving up in disgust, this method should not be called at all on the
     * corresponding CIT.
     * 
     * @param when
     *            the time at which the task was successfully completed.
     * @throws IllegalArgumentException
     *             if 'when' is less than the CIT's getArrivalTime()
     * @throws IllegalStateException
     *             if the method has already been called on this CIT.
     * 
     */
    public void setTaskCompleteAt(double when) {
        if (when < this.getArrivalTime())
            throw new IllegalArgumentException("Illegal completion time");
        if (taskCompletedAt != -1)
            throw new IllegalStateException("Task already completed");
        this.taskCompletedAt = when;
    }

    /**
     * Check if the customer corresponding to this CIT ever managed to enter the
     * DMV at all. The DMV (by using setEntered) should arrange for this should
     * to true in the 'normal' case -- when customers go into the DMV and are
     * perhaps served, or not -- and this should only return false if the DMV
     * was filled to capacity when this CIT was first delivered to the Door.
     * (Note that the DMV may never need to call this method.)
     * 
     * @return true if the customer of this CIT entered the building (whether or
     *         not they were subsequently served successfully.)
     * @since 1.0
     */
    public boolean getEntered() {
        return entered;
    }

    /**
     * Set the value of the 'customer ever entered the building' flag on this
     * CIT. Normally this would called (with an argument of 'true') by the DMV
     * Door as soon as it determined there is room in the DMV for the customer
     * corresponding to this CIT. An argument of 'false' would be used only if
     * the DMV was filled to capacity when this CIT was first delivered to the
     * Door -- and the CIT is going to be immediately returned to the World
     * without further processing. <b>Note that it is the DMV's responsibility
     * to call 'cit.setEntered(true)' on every CIT whose corresponding customer
     * actually entered the DMV!</b>
     * 
     * @param entered
     *            whether or not the customer corresponding to this CIT entered
     *            the DMV. (whether or not they were subsequently served
     *            successfully.)
     * @since 1.0
     */
    public void setEntered(boolean entered) {
        this.entered = entered;
    }

}
